<?php
/**
 * @author all@cosmos-framework.org
 * @copyright (c) 2013 cosmos-framework.org
 * @license http://www.opensource.org/licenses/mit-license.php
 * @link http://cosmos-framework.org/
 * @package cosmos.framework
 * @version 2.0
 */
namespace Cosmos;

class SQL {

    private $_statement;

    /**
     * @param mixed $v
     * @return mixed
     */
    public static function quote($v) {

        if (is_string($v)) {

            // response '', \\.
            $v = str_replace('\'', '\'\'', $v);
            $v = str_replace('\\', '\\\\', $v);
            $v = sprintf("'%s'", $v);
        }

        return $v ? : '';
    }

    /**
     * @param string $module
     * @param mixed[] $fields
     * @return string
     */
    public static function requires($module, $fields = array()) {

        return self::_require($module)->compile($fields);
    }

    /**
     * @param string $statement
     */
    public function __construct($statement) {

        $this->_statement = preg_replace('/\/\*(.*?)\*\//s', '', $statement);
    }

    /**
     * @param mixed[] $fields
     * @return string
     */
    public function compile($fields = array()) {

        $_ = $this->_statement;

        // insert into (:?) values (:*)
        $_ = preg_replace_callback('/:\?/', function() use ($fields) {

            $response = array();
            foreach (Set::keys($fields) as $_) {
                $response[] = '`' . $_ . '`';
            }

            // response serialized.
            return implode(',', $response);

        }, $_);

        // insert into (:?) values (:*)
        $_ = preg_replace_callback('/:\*/', function() use ($fields) {

            $response = array();
            foreach (Set::keys($fields) as $_) {
                $response[] = ':' . $_;
            }

            // response serialized.
            return implode(',', $response);

        }, $_);

        // select * from where =:name
        $_ = preg_replace('/([&\|,]?)\s*?(!?)([<>]?[<>=])([\$:])(\w+)/', '/$1 $2 `$5`$3/$4$5', $_);

        // select * from where & in(:name)
        $_ = preg_replace('/([&\|]?)\s*?(!?)IN :(\w+)/', '/$1 `$3` $2 IN/:$3', $_);

        // select * from where like :name
        $_ = preg_replace('/([&\|]?)\s*?(!?)LIKE :(\w+)/', '/$1 `$3` $2 LIKE /:$3', $_);

        // select * from limit $number
        $_ = preg_replace(array('/LIMIT \$(\w+)/', '/OFFSET \$(\w+)/'), array('/LIMIT /\$$1', '/OFFSET /\$$1'), $_);

        // select * from where 1 & =:name > select * from where 1
        $_ = preg_replace_callback('/\/(.*?)\/:(\w+)/', function($_) use ($fields) {

            if (isset($fields[$_[2]])) {

                // enable response.
                return sprintf('%s:%s', $_[1], $_[2]);
            }

        }, $_);

        // select * from limit $number/l/u
        $_ = preg_replace_callback('/\/(.*?)\/\$(\w+)\/(\d+)\/(\d+)/', function($_) use ($fields) {

            if (isset($fields[$_[2]])) {

                // response l < number < u.
                return $_[1] . Math::limit(floatval($fields[$_[2]]), $_[3], $_[4]);
            }

        }, $_);

        // select * from limit $number
        $_ = preg_replace_callback('/\/(.*?)\/\$(\w+)/', function($_) use ($fields) {

            if (isset($fields[$_[2]])) {

                // response number.
                return $_[1] . floatval($fields[$_[2]]);
            }

        }, $_);

        $_ = str_replace(array('!', '&', '|'), array('NOT', 'AND', 'OR'), $_);

        // select * from where in(:name)
        $_ = preg_replace(array('/\s+/', '/IN :(\w+)/'), array(' ', 'IN(:$1)'), $_);

        // select * from where =:name
        $_ = preg_replace_callback('/:(\w+)/', function($_) use ($fields) {

            if (is_array($fields[$_[1]])) {

                // response [].
                return implode(',', array_map('self::quote', $fields[$_[1]]));
            }
            // a response.
            return self::quote($fields[$_[1]]);

        }, $_);

        return $_;
    }

    /**
     * @access private
     */
    private static function _require($module) {

        return new self(file_get_contents($module, true));
    }

}
